"""
SOP Service Layer
=================

This module implements the SOP (Standard Operating Procedure) service layer
providing business logic for SOP operations.
"""

import logging
from typing import List, Optional, Dict, Any

from ..domain.sop import SOP
from ..domain.sop_repository import SOPRepository
from ..infrastructure.sqlite_sop_repository import SQLiteSOPRepository

logger = logging.getLogger(__name__)


class SOPService:
    """
    Service layer for SOP business operations.
    
    Provides business logic and coordinates between domain entities
    and repository implementations.
    """
    
    def __init__(self, repository: SOPRepository):
        """
        Initialize the SOP service.
        
        Args:
            repository: The SOP repository instance
        """
        self.repository = repository
    
    def create_sop(self, subject: str, sop_content: str, created_by: str) -> SOP:
        """
        Create a new SOP.
        
        Args:
            subject: The subject of the SOP
            sop_content: The content of the SOP
            created_by: The creator of the SOP
            
        Returns:
            The created SOP entity
            
        Raises:
            ValueError: If required fields are missing or invalid
        """
        try:
            # Validate input
            if not subject or not subject.strip():
                raise ValueError("Subject cannot be empty")
            if not sop_content or not sop_content.strip():
                raise ValueError("SOP content cannot be empty")
            if not created_by or not created_by.strip():
                raise ValueError("Created by cannot be empty")
            
            # Create SOP entity
            sop = SOP(
                subject=subject,
                sop=sop_content,
                created_by=created_by
            )
            
            # Save to repository
            saved_sop = self.repository.save(sop)
            
            logger.info(f"Created new SOP: {saved_sop}")
            return saved_sop
            
        except Exception as e:
            logger.error(f"Failed to create SOP: {e}")
            raise
    
    def get_sop_by_id(self, sop_id: int) -> Optional[SOP]:
        """
        Get a SOP by its ID.
        
        Args:
            sop_id: The ID of the SOP to retrieve
            
        Returns:
            The SOP entity if found, None otherwise
        """
        try:
            if not isinstance(sop_id, int) or sop_id <= 0:
                raise ValueError("Invalid SOP ID")
            
            sop = self.repository.find_by_id(sop_id)
            
            if sop:
                logger.info(f"Retrieved SOP: {sop}")
            else:
                logger.info(f"SOP not found: ID {sop_id}")
            
            return sop
            
        except Exception as e:
            logger.error(f"Failed to get SOP by ID {sop_id}: {e}")
            raise
    
    def get_all_sops(self) -> List[SOP]:
        """
        Get all SOPs.
        
        Returns:
            List of all SOP entities
        """
        try:
            sops = self.repository.find_all()
            logger.info(f"Retrieved {len(sops)} SOPs")
            return sops
            
        except Exception as e:
            logger.error(f"Failed to get all SOPs: {e}")
            raise
    
    def update_sop(self, sop_id: int, subject: str = None, sop_content: str = None, created_by: str = None) -> SOP:
        """
        Update an existing SOP.
        
        Args:
            sop_id: The ID of the SOP to update
            subject: New subject (optional)
            sop_content: New SOP content (optional)
            created_by: New creator (optional)
            
        Returns:
            The updated SOP entity
            
        Raises:
            ValueError: If the SOP doesn't exist or invalid data
        """
        try:
            # Get existing SOP
            existing_sop = self.repository.find_by_id(sop_id)
            if not existing_sop:
                raise ValueError(f"SOP with ID {sop_id} not found")
            
            # Update fields if provided
            if subject is not None:
                if not subject.strip():
                    raise ValueError("Subject cannot be empty")
                existing_sop.subject = subject.strip()
            
            if sop_content is not None:
                if not sop_content.strip():
                    raise ValueError("SOP content cannot be empty")
                existing_sop.sop = sop_content.strip()
            
            if created_by is not None:
                if not created_by.strip():
                    raise ValueError("Created by cannot be empty")
                existing_sop.created_by = created_by.strip()
            
            # Save changes
            updated_sop = self.repository.update(existing_sop)
            
            logger.info(f"Updated SOP: {updated_sop}")
            return updated_sop
            
        except Exception as e:
            logger.error(f"Failed to update SOP {sop_id}: {e}")
            raise
    
    def delete_sop(self, sop_id: int) -> bool:
        """
        Delete a SOP.
        
        Args:
            sop_id: The ID of the SOP to delete
            
        Returns:
            True if the SOP was deleted, False if it didn't exist
        """
        try:
            if not isinstance(sop_id, int) or sop_id <= 0:
                raise ValueError("Invalid SOP ID")
            
            deleted = self.repository.delete(sop_id)
            
            if deleted:
                logger.info(f"Deleted SOP: ID {sop_id}")
            else:
                logger.info(f"SOP not found for deletion: ID {sop_id}")
            
            return deleted
            
        except Exception as e:
            logger.error(f"Failed to delete SOP {sop_id}: {e}")
            raise
    
    def search_sops_by_subject(self, subject: str) -> List[SOP]:
        """
        Search SOPs by subject.
        
        Args:
            subject: The subject to search for
            
        Returns:
            List of matching SOP entities
        """
        try:
            if not subject or not subject.strip():
                raise ValueError("Search subject cannot be empty")
            
            sops = self.repository.find_by_subject(subject.strip())
            
            logger.info(f"Found {len(sops)} SOPs matching subject '{subject}'")
            return sops
            
        except Exception as e:
            logger.error(f"Failed to search SOPs by subject '{subject}': {e}")
            raise
    
    def get_sops_by_creator(self, created_by: str) -> List[SOP]:
        """
        Get SOPs by creator.
        
        Args:
            created_by: The creator to filter by
            
        Returns:
            List of SOP entities created by the specified creator
        """
        try:
            if not created_by or not created_by.strip():
                raise ValueError("Creator cannot be empty")
            
            sops = self.repository.find_by_creator(created_by.strip())
            
            logger.info(f"Found {len(sops)} SOPs created by '{created_by}'")
            return sops
            
        except Exception as e:
            logger.error(f"Failed to get SOPs by creator '{created_by}': {e}")
            raise
    
    def get_sops_as_dict_list(self) -> List[Dict[str, Any]]:
        """
        Get all SOPs as a list of dictionaries.
        
        Returns:
            List of SOP dictionaries
        """
        try:
            sops = self.get_all_sops()
            return [sop.to_dict() for sop in sops]
            
        except Exception as e:
            logger.error(f"Failed to get SOPs as dict list: {e}")
            raise
    
    def get_sop_by_id_as_dict(self, sop_id: int) -> Optional[Dict[str, Any]]:
        """
        Get a SOP by ID as a dictionary.
        
        Args:
            sop_id: The ID of the SOP to retrieve
            
        Returns:
            The SOP dictionary if found, None otherwise
        """
        try:
            sop = self.get_sop_by_id(sop_id)
            return sop.to_dict() if sop else None
            
        except Exception as e:
            logger.error(f"Failed to get SOP by ID {sop_id} as dict: {e}")
            raise
    
    def get_sop_by_ids(self, sop_ids: List[int]) -> List[Dict[str, Any]]:
        """
        Get SOP data by IDs and return as list of dictionaries.
        
        Args:
            sop_ids: List of SOP IDs to retrieve
            
        Returns:
            List of SOP dictionaries
        """
        try:
            if not sop_ids:
                return []
            
            # Validate IDs
            valid_ids = []
            for sop_id in sop_ids:
                if isinstance(sop_id, int) and sop_id > 0:
                    valid_ids.append(sop_id)
            
            if not valid_ids:
                return []
            
            # Get SOPs from repository
            sops = self.repository.find_by_ids(valid_ids)
            
            # Convert to dictionaries
            result = []
            for sop in sops:
                sop_dict = {
                    'id': sop.id,
                    'subject': sop.subject,
                    'sop': sop.sop,
                    'created_at': sop.created_at.isoformat(),
                    'created_by': sop.created_by
                }
                result.append(sop_dict)
            
            logger.info(f"Retrieved {len(result)} SOPs by IDs")
            return result
            
        except Exception as e:
            logger.error(f"Failed to get SOPs by IDs {sop_ids}: {e}")
            raise